import {Euler, EventDispatcher, Vector3, Spherical} from 'three';
import {TWEEN} from "three/examples/jsm/libs/tween.module.min.js";

var MoveDirectionControls = function (camera, domElement) {
  if (domElement === undefined) {
    console.warn('THREE.MoveDirectionControls: The second parameter "domElement" is now mandatory.');
    domElement = document.body;
  }

  this.customParams = {}

  this.domElement = domElement;

  // Set to constrain the pitch of the camera 设置为限制摄像机的俯仰
  // Range is 0 to Math.PI radians 范围是0到Math.PI弧度
  this.minPolarAngle = 0; // radians
  this.maxPolarAngle = Math.PI; // radians

  // internals
  var scope = this;

  var gravity = 9.8
  var cameraH = camera.position.y

  var mouseSlide = false
  var startX, startY;
  var euler = new Euler(0, 0, 0, 'YXZ');
  var v = .002; // 视角转动速度

  var spherical = new Spherical();

  var PI_2 = Math.PI / 2;

  var vec = new Vector3();

  var moveForward = false,
    moveBackward = false,
    moveLeft = false,
    moveRight = false,
    canJump = true,
    moveV = 1;

  this.startFun = undefined
  this.moveFun = undefined
  this.endFun = undefined

  function start(e) {
    scope.domElement.setAttribute('tabindex', '0')
    if (scope.startFun) scope.startFun(e)

    startX = e.pageX || e.touches[0].pageX
    startY = e.pageY || e.touches[0].pageY
  }

  function move(e) {
    if (scope.moveFun) scope.moveFun(e)

    if (mouseSlide) {

      euler.setFromQuaternion(camera.quaternion);
      const pageX = e.pageX || e.touches[0].pageX, pageY = e.pageY || e.touches[0].pageY

      euler.y -= (startX - pageX) * v;
      euler.x -= (startY - pageY) * v;
      euler.x = Math.max(-PI_2, Math.min(PI_2, euler.x));
      // euler.x = Math.max(PI_2 - scope.maxPolarAngle, Math.min(PI_2 - scope.minPolarAngle, euler.x));
      camera.quaternion.setFromEuler(euler);
      startX = pageX
      startY = pageY
    }
  }

  function end(e) {
    if (scope.endFun) scope.endFun(e)
  }

  {
    scope.domElement.addEventListener('keydown', onKeyDown, false)
    scope.domElement.addEventListener('keyup', onKeyUp, false)

    scope.domElement.addEventListener('touchstart', start, false)
    scope.domElement.addEventListener('touchmove', move, false)
    scope.domElement.addEventListener('touchend', end, false)
    scope.domElement.addEventListener('mousedown', function (ev) {
      start(ev)
      mouseSlide = true
      scope.domElement.addEventListener('mousemove', move, false)
    }, false)
    scope.domElement.addEventListener('mouseup', function (e) {
      mouseSlide = false
      end(e)
    }, false)
    scope.domElement.addEventListener('mouseout', function () {
      mouseSlide = false
    }, false)
  }

  this.dispose = function () {
    scope.domElement.removeEventListener('keydown', onKeyDown)
    scope.domElement.removeEventListener('keyup', onKeyUp)

    scope.domElement.removeEventListener('touchstart', start);
    scope.domElement.removeEventListener('touchmove', move);
    scope.domElement.removeEventListener('touchend', end)

    scope.domElement.removeEventListener('mousedown', start);
    scope.domElement.removeEventListener('mousemove', move);
    scope.domElement.removeEventListener('mouseup', end)
  };

  this.getObject = function () { // retaining this method for backward compatibility 保留此方法以实现向后兼容
    return camera;
  };

  this.getDirection = function () {
    spherical.theta = euler.x
    spherical.phi = euler.y
    return spherical
  };

  function jumpCamera(camera, cameraH, gravity, time, tween) {
    const jumpH = 9.8 * time - gravity * time * time / 2
    if (jumpH <= 0) {
      camera.position.setY(cameraH)
    } else {
      camera.position.setY(cameraH + jumpH)
    }
  }

  function onKeyDown(event) {
    switch (event.code) {
      case 'ArrowUp':
      case 'KeyW':
        moveForward = true;
        break;

      case 'ArrowLeft':
      case 'KeyA':
        moveLeft = true;
        break;

      case 'ArrowDown':
      case 'KeyS':
        moveBackward = true;
        break;

      case 'ArrowRight':
      case 'KeyD':
        moveRight = true;
        break;

      case 'Space':
        if (canJump === true) {
          new TWEEN.Tween({time: 0})
            .to({time: 1000})
            .onUpdate(val => {
              jumpCamera(camera, cameraH, gravity, val.time / 500)
            }).onComplete(val => {
            canJump = true
          }).start();
        }
        canJump = false;
        break;

      case 'ShiftLeft':
      case 'ShiftRight':
        moveV = 3;
        break;
    }
  };

  function onKeyUp(event) {

    switch (event.code) {

      case 'ArrowUp':
      case 'KeyW':
        moveForward = false;
        break;

      case 'ArrowLeft':
      case 'KeyA':
        moveLeft = false;
        break;

      case 'ArrowDown':
      case 'KeyS':
        moveBackward = false;
        break;

      case 'ArrowRight':
      case 'KeyD':
        moveRight = false;
        break;

      case 'ShiftLeft':
      case 'ShiftRight':
        moveV = 1;
        break;
    }

  };

  this.moveForward = function (distance) {
    vec.setFromMatrixColumn(camera.matrix, 0);
    vec.crossVectors(camera.up, vec);
    camera.position.addScaledVector(vec, distance * moveV);
  };

  this.moveRight = function (distance) {
    vec.setFromMatrixColumn(camera.matrix, 0);
    camera.position.addScaledVector(vec, distance * moveV);
  };

  this.getPosition = function () {
    return camera.position
  }

  this.fromMoveTo = function (from, to, time) {
    new TWEEN.Tween(from)
      .to(to, time || 1000)
      .easing(TWEEN.Easing.Sinusoidal.InOut).start();
  }

  this.update = function () {

    const forward = moveForward ? .1 : moveBackward ? -.1 : 0;
    const left = moveLeft ? -.1 : moveRight ? .1 : 0;
    if (forward) scope.moveForward(forward);
    if (left) scope.moveRight(left);

    TWEEN.update()
  }
};

MoveDirectionControls.prototype = Object.create(EventDispatcher.prototype);
MoveDirectionControls.prototype.constructor = MoveDirectionControls;

export {MoveDirectionControls};
